/*----------------------------------------------------------------------------/
 /  Petit FatFs - FAT file system module  R0.02a                (C)ChaN, 2010
 /-----------------------------------------------------------------------------/
 / Petit FatFs module is an open source software to implement FAT file system to
 / small embedded systems. This is a free software and is opened for education,
 / research and commercial developments under license policy of following trems.
 /
 /  Copyright (C) 2010, ChaN, all right reserved.
 /
 / * The Petit FatFs module is a free software and there is NO WARRANTY.
 / * No restriction on use. You can use, modify and redistribute it for
 /   personal, non-profit or commercial use UNDER YOUR RESPONSIBILITY.
 / * Redistributions of source code must retain the above copyright notice.
 /
 /-----------------------------------------------------------------------------/
 / Jun 15,'09  R0.01a  First release. (Branched from FatFs R0.07b.)
 /
 / Dec 14,'09  R0.02   Added multiple code page support.
 /                     Added write funciton.
 /                     Changed stream read mode interface.
 / Dec 07,'10  R0.02a  Added some configuration options.
 /                     Fixed fails to open objects with DBCS character.
 /----------------------------------------------------------------------------*/

#include "pff.h"		/* Petit FatFs configurations and declarations */
#include "diskio.h"		/* Declarations of low level disk I/O functions */

/*--------------------------------------------------------------------------

 Module Private Definitions

 ---------------------------------------------------------------------------*/

#if _FS_FAT32
#define LD_CLUST(dir)	(((uint32_t)LD_WORD(dir+DIR_FstClusHI)<<16) | LD_WORD(dir+DIR_FstClusLO))
#else
#define LD_CLUST(dir)	LD_WORD(dir+DIR_FstClusLO)
#endif

/*--------------------------------------------------------*/
/* DBCS code ranges and SBCS extend char conversion table */

#if _CODE_PAGE == 932	/* Japanese Shift-JIS */
#define _DF1S	0x81	/* DBC 1st byte range 1 start */
#define _DF1E	0x9F	/* DBC 1st byte range 1 end */
#define _DF2S	0xE0	/* DBC 1st byte range 2 start */
#define _DF2E	0xFC	/* DBC 1st byte range 2 end */
#define _DS1S	0x40	/* DBC 2nd byte range 1 start */
#define _DS1E	0x7E	/* DBC 2nd byte range 1 end */
#define _DS2S	0x80	/* DBC 2nd byte range 2 start */
#define _DS2E	0xFC	/* DBC 2nd byte range 2 end */

#elif _CODE_PAGE == 936	/* Simplified Chinese GBK */
#define _DF1S	0x81
#define _DF1E	0xFE
#define _DS1S	0x40
#define _DS1E	0x7E
#define _DS2S	0x80
#define _DS2E	0xFE

#elif _CODE_PAGE == 949	/* Korean */
#define _DF1S	0x81
#define _DF1E	0xFE
#define _DS1S	0x41
#define _DS1E	0x5A
#define _DS2S	0x61
#define _DS2E	0x7A
#define _DS3S	0x81
#define _DS3E	0xFE

#elif _CODE_PAGE == 950	/* Traditional Chinese Big5 */
#define _DF1S	0x81
#define _DF1E	0xFE
#define _DS1S	0x40
#define _DS1E	0x7E
#define _DS2S	0xA1
#define _DS2E	0xFE

#elif _CODE_PAGE == 437	/* U.S. (OEM) */
#define _DF1S	0
#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
				0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

#elif _CODE_PAGE == 720	/* Arabic (OEM) */
#define _DF1S	0
#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
				0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

#elif _CODE_PAGE == 737	/* Greek (OEM) */
#define _DF1S	0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
				0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

#elif _CODE_PAGE == 775	/* Baltic (OEM) */
#define _DF1S	0
#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
				0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

#elif _CODE_PAGE == 850	/* Multilingual Latin 1 (OEM) */
#define _DF1S	0
#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
				0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

#elif _CODE_PAGE == 852	/* Latin 2 (OEM) */
#define _DF1S	0
#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \
				0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}

#elif _CODE_PAGE == 855	/* Cyrillic (OEM) */
#define _DF1S	0
#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
				0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
				0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}

#elif _CODE_PAGE == 857	/* Turkish (OEM) */
#define _DF1S	0
#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
				0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

#elif _CODE_PAGE == 858	/* Multilingual Latin 1 + Euro (OEM) */
#define _DF1S	0
#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
				0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

#elif _CODE_PAGE == 862	/* Hebrew (OEM) */
#define _DF1S	0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
				0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

#elif _CODE_PAGE == 866	/* Russian (OEM) */
#define _DF1S	0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
				0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

#elif _CODE_PAGE == 874	/* Thai (OEM, Windows) */
#define _DF1S	0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
				0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */
#define _DF1S	0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
				0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}

#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */
#define _DF1S	0
#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
				0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF}

#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */
#define _DF1S	0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \
				0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}

#elif _CODE_PAGE == 1253 /* Greek (Windows) */
#define _DF1S	0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
				0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \
				0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF}

#elif _CODE_PAGE == 1254 /* Turkish (Windows) */
#define _DF1S	0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \
				0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}

#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */
#define _DF1S	0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
				0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

#elif _CODE_PAGE == 1256 /* Arabic (Windows) */
#define _DF1S	0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \
				0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF}

#elif _CODE_PAGE == 1257 /* Baltic (Windows) */
#define _DF1S	0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
				0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}

#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */
#define _DF1S	0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \
				0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
				0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F}

#elif _CODE_PAGE == 1	/* ASCII (for only non-LFN cfg) */
#define _DF1S	0

#else
#error Unknown code page

#endif

/* Character code support macros */

#define IsUpper(c)	(((c)>='A')&&((c)<='Z'))
#define IsLower(c)	(((c)>='a')&&((c)<='z'))

#if _DF1S		/* DBCS configuration */

#ifdef _DF2S	/* Two 1st byte areas */
#define IsDBCS1(c)	(((uint8_t)(c) >= _DF1S && (uint8_t)(c) <= _DF1E) || ((uint8_t)(c) >= _DF2S && (uint8_t)(c) <= _DF2E))
#else			/* One 1st byte area */
#define IsDBCS1(c)	((uint8_t)(c) >= _DF1S && (uint8_t)(c) <= _DF1E)
#endif

#ifdef _DS3S	/* Three 2nd byte areas */
#define IsDBCS2(c)	(((uint8_t)(c) >= _DS1S && (uint8_t)(c) <= _DS1E) || ((uint8_t)(c) >= _DS2S && (uint8_t)(c) <= _DS2E) || ((uint8_t)(c) >= _DS3S && (uint8_t)(c) <= _DS3E))
#else			/* Two 2nd byte areas */
#define IsDBCS2(c)	(((uint8_t)(c) >= _DS1S && (uint8_t)(c) <= _DS1E) || ((uint8_t)(c) >= _DS2S && (uint8_t)(c) <= _DS2E))
#endif

#else			/* SBCS configuration */

#define IsDBCS1(c)	0
#define IsDBCS2(c)	0

#endif /* _DF1S */

/* FatFs refers the members in the FAT structures with byte offset instead
 / of structure member because there are incompatibility of the packing option
 / between various compilers. */

#define BS_jmpBoot			0
#define BS_OEMName			3
#define BPB_BytsPerSec		11
#define BPB_SecPerClus		13
#define BPB_RsvdSecCnt		14
#define BPB_NumFATs			16
#define BPB_RootEntCnt		17
#define BPB_TotSec16		19
#define BPB_Media			21
#define BPB_FATSz16			22
#define BPB_SecPerTrk		24
#define BPB_NumHeads		26
#define BPB_HiddSec			28
#define BPB_TotSec32		32
#define BS_55AA				510

#define BS_DrvNum			36
#define BS_BootSig			38
#define BS_VolID			39
#define BS_VolLab			43
#define BS_FilSysType		54

#define BPB_FATSz32			36
#define BPB_ExtFlags		40
#define BPB_FSVer			42
#define BPB_RootClus		44
#define BPB_FSInfo			48
#define BPB_BkBootSec		50
#define BS_DrvNum32			64
#define BS_BootSig32		66
#define BS_VolID32			67
#define BS_VolLab32			71
#define BS_FilSysType32		82

#define MBR_Table			446

#define	DIR_Name			0
#define	DIR_Attr			11
#define	DIR_NTres			12
#define	DIR_CrtTime			14
#define	DIR_CrtDate			16
#define	DIR_FstClusHI		20
#define	DIR_WrtTime			22
#define	DIR_WrtDate			24
#define	DIR_FstClusLO		26
#define	DIR_FileSize		28

/*--------------------------------------------------------------------------

 Private Functions

 ---------------------------------------------------------------------------*/

static FATFS *FatFs; /* Pointer to the file system object (logical drive) */

/* Fill memory */
static void mem_set(void* dst, int val, int cnt) {
	char *d = (char*) dst;
	while (cnt--)
		*d++ = (char) val;
}

/* Compare memory to memory */
static int mem_cmp(const void* dst, const void* src, int cnt) {
	const char *d = (const char *) dst, *s = (const char *) src;
	int r = 0;
	while (cnt-- && (r = *d++ - *s++) == 0)
		;
	return r;
}

/*-----------------------------------------------------------------------*/
/* FAT access - Read value of a FAT entry                                */
/*-----------------------------------------------------------------------*/

/* 1:IO error, Else:Cluster status */
/* Cluster# to get the link information */
static CLUST get_fat(CLUST clst) {
	uint16_t wc, bc, ofs;
	uint8_t* buf;
	FATFS *fs = FatFs;

	if (clst < 2 || clst >= fs->n_fatent) /* Range check */
		return 1;

	switch (fs->fs_type) {
#if _FS_FAT12
		case FS_FAT12:
			bc = (uint16_t) clst;
			bc += bc / 2;
			ofs = bc % 512;
			bc /= 512;
			if (ofs != 511) {
				if (disk_readp_nocopy(&buf, fs->fatbase + bc, ofs, 2))
					break;
			}
			else {
				if (disk_readp_nocopy(&buf, fs->fatbase + bc, 511, 1))
					break;
				if (disk_readp_nocopy(&buf + 1, fs->fatbase + bc + 1, 0, 1))
					break;
			}
			wc = LD_WORD(buf);
			return (clst & 1) ? (wc >> 4) : (wc & 0xFFF);
#endif
		case FS_FAT16:
			if (disk_readp_nocopy(&buf, fs->fatbase + clst / 256, (uint16_t)(((uint16_t) clst % 256) * 2), 2))
				break;
			return LD_WORD(buf);
#if _FS_FAT32
		case FS_FAT32:
			if (disk_readp_nocopy(&buf, fs->fatbase + clst / 128, (uint16_t)(((uint16_t) clst % 128) * 4), 4))
				break;
			return LD_DWORD(buf) & 0x0FFFFFFF;
#endif
	}

	return 1; /* An error occured at the disk I/O layer */
}

/*-----------------------------------------------------------------------*/
/* Get sector# from cluster#                                             */
/*-----------------------------------------------------------------------*/

/* !=0: Sector number, 0: Failed - invalid cluster# */
/* Cluster# to be converted */
static uint32_t clust2sect(CLUST clst) {
	FATFS *fs = FatFs;

	clst -= 2;
	if (clst >= (fs->n_fatent - 2))
		return 0; /* Invalid cluster# */
	return (uint32_t) clst * fs->csize + fs->database;
}

/*-----------------------------------------------------------------------*/
/* Directory handling - Rewind directory index                           */
/*-----------------------------------------------------------------------*/

/* Pointer to directory object */
static FRESULT dir_rewind(DIR *dj) {
	CLUST clst;
	FATFS *fs = FatFs;

	dj->index = 0;
	clst = dj->sclust;
	if (clst == 1 || clst >= fs->n_fatent) /* Check start cluster range */
		return FR_DISK_ERR;
	if (_FS_FAT32 && !clst && fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */
		clst = (CLUST) fs->dirbase;
	dj->clust = clst; /* Current cluster */
	dj->sect = clst ? clust2sect(clst) : fs->dirbase; /* Current sector */

	return FR_OK; /* Seek succeeded */
}

/*-----------------------------------------------------------------------*/
/* Directory handling - Move directory index next                        */
/*-----------------------------------------------------------------------*/

/* FR_OK:Succeeded, FR_NO_FILE:End of table */
/* Pointer to directory object */
static FRESULT dir_next(DIR *dj) {
	CLUST clst;
	uint16_t i;
	FATFS *fs = FatFs;

	i = dj->index + 1;
	if (!i || !dj->sect) /* Report EOT when index has reached 65535 */
		return FR_NO_FILE;

	if (!(i % 16)) { /* Sector changed? */
		dj->sect++; /* Next sector */

		if (dj->clust == 0) { /* Static table */
			if (i >= fs->n_rootdir) /* Report EOT when end of table */
				return FR_NO_FILE;
		}
		else { /* Dynamic table */
			if (((i / 16) & (fs->csize - 1)) == 0) { /* Cluster changed? */
				clst = get_fat(dj->clust); /* Get next cluster */
				if (clst <= 1)
					return FR_DISK_ERR;
				if (clst >= fs->n_fatent) /* When it reached end of dynamic table */
					return FR_NO_FILE; /* Report EOT */
				dj->clust = clst; /* Initialize data for new cluster */
				dj->sect = clust2sect(clst);
			}
		}
	}

	dj->index = i;

	return FR_OK;
}

/*-----------------------------------------------------------------------*/
/* Directory handling - Find an object in the directory                  */
/*-----------------------------------------------------------------------*/

/* Pointer to the directory object linked to the file name */
/* 32-byte working buffer */
static FRESULT dir_find(DIR *dj, uint8_t *dir) {
	FRESULT res;
	uint8_t c;

	res = dir_rewind(dj); /* Rewind directory object */
	if (res != FR_OK)
		return res;

	do {
		res = disk_readp(dir, dj->sect, (uint16_t)((dj->index % 16) * 32), 32) /* Read an entry */
		? FR_DISK_ERR : FR_OK;
		if (res != FR_OK)
			break;
		c = dir[DIR_Name]; /* First character */
		if (c == 0) {
			res = FR_NO_FILE;
			break;
		} /* Reached to end of table */
		if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */
			break;
		res = dir_next(dj); /* Next entry */
	} while (res == FR_OK);

	return res;
}

/*-----------------------------------------------------------------------*/
/* Read an object from the directory                                     */
/*-----------------------------------------------------------------------*/
#if _USE_DIR
/* Pointer to the directory object to store read object name */
/* 32-byte working buffer */
static FRESULT dir_read(DIR *dj, uint8_t *dir) {
	FRESULT res;
	uint8_t a, c;

	res = FR_NO_FILE;
	while (dj->sect) {
		res = disk_readp(dir, dj->sect, (uint16_t)((dj->index % 16) * 32), 32) /* Read an entry */
		? FR_DISK_ERR : FR_OK;
		if (res != FR_OK)
			break;
		c = dir[DIR_Name];
		if (c == 0) {
			res = FR_NO_FILE;
			break;
		} /* Reached to end of table */
		a = dir[DIR_Attr] & AM_MASK;
		if (c != 0xE5 && c != '.' && !(a & AM_VOL)) /* Is it a valid entry? */
			break;
		res = dir_next(dj); /* Next entry */
		if (res != FR_OK)
			break;
	}

	if (res != FR_OK)
		dj->sect = 0;

	return res;
}
#endif

/*-----------------------------------------------------------------------*/
/* Pick a segment and create the object name in directory form           */
/*-----------------------------------------------------------------------*/

#ifdef _EXCVT
static const uint8_t cvt[] = _EXCVT;
#endif

/* Pointer to the directory object */
/* Pointer to pointer to the segment in the path string */
static FRESULT create_name(DIR *dj, const char **path) {
	uint8_t c, d, ni, si, i, *sfn;
	const char *p;

	/* Create file name in directory form */
	sfn = dj->fn;
	mem_set(sfn, ' ', 11);
	si = i = 0;
	ni = 8;
	p = *path;
	for (;;) {
		c = p[si++];
		if (c <= ' ' || c == '/')
			break; /* Break on end of segment */
		if (c == '.' || i >= ni) {
			if (ni != 8 || c != '.')
				break;
			i = 8;
			ni = 11;
			continue;
		}
#ifdef _EXCVT
		if (c >= 0x80) /* To upper extended char (SBCS) */
		c = cvt[c - 0x80];
#endif
		if (IsDBCS1(c) && i < ni - 1) { /* DBC 1st byte? */
			d = p[si++]; /* Get 2nd byte */
			sfn[i++] = c;
			sfn[i++] = d;
		}
		else { /* Single byte code */
			if (IsLower(c))
				c -= 0x20; /* toupper */
			sfn[i++] = c;
		}
	}
	*path = &p[si]; /* Rerurn pointer to the next segment */

	sfn[11] = (c <= ' ') ? 1 : 0; /* Set last segment flag if end of path */

	return FR_OK;
}

/*-----------------------------------------------------------------------*/
/* Get file information from directory entry                             */
/*-----------------------------------------------------------------------*/
#if _USE_DIR
/* No return code */
/* Pointer to the directory object */
/* 32-byte working buffer */
/* Pointer to store the file information */
static
void get_fileinfo(DIR *dj, uint8_t *dir, FILINFO *fno) {
	uint8_t i, c;
	char *p;

	p = fno->fname;
	if (dj->sect) {
		for (i = 0; i < 8; i++) { /* Copy file name body */
			c = dir[i];
			if (c == ' ')
				break;
			if (c == 0x05)
				c = 0xE5;
			*p++ = c;
		}
		if (dir[8] != ' ') { /* Copy file name extension */
			*p++ = '.';
			for (i = 8; i < 11; i++) {
				c = dir[i];
				if (c == ' ')
					break;
				*p++ = c;
			}
		}
		fno->fattrib = dir[DIR_Attr]; /* Attribute */
		fno->fsize = LD_DWORD(dir+DIR_FileSize); /* Size */
		fno->fdate = LD_WORD(dir+DIR_WrtDate); /* Date */
		fno->ftime = LD_WORD(dir+DIR_WrtTime); /* Time */
	}
	*p = 0;
}
#endif /* _USE_DIR */

/*-----------------------------------------------------------------------*/
/* Follow a file path                                                    */
/*-----------------------------------------------------------------------*/
/* FR_OK(0): successful, !=0: error code */
/* Directory object to return last directory and found object */
/* 32-byte working buffer */
/* Full-path string to find a file or directory */
static FRESULT follow_path(DIR *dj, uint8_t *dir, const char *path) {
	FRESULT res;

	while (*path == ' ')
		path++; /* Skip leading spaces */
	if (*path == '/')
		path++; /* Strip heading separator */
	dj->sclust = 0; /* Set start directory (always root dir) */

	if ((uint8_t) * path <= ' ') { /* Null path means the root directory */
		res = dir_rewind(dj);
		dir[0] = 0;

	}
	else { /* Follow path */
		for (;;) {
			res = create_name(dj, &path); /* Get a segment */
			if (res != FR_OK)
				break;
			res = dir_find(dj, dir); /* Find it */
			if (res != FR_OK) { /* Could not find the object */
				if (res == FR_NO_FILE && !*(dj->fn + 11))
					res = FR_NO_PATH;
				break;
			}
			if (*(dj->fn + 11))
				break; /* Last segment match. Function completed. */
			if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */
				res = FR_NO_PATH;
				break;
			}
			dj->sclust = LD_CLUST(dir);
		}
	}

	return res;
}

/*-----------------------------------------------------------------------*/
/* Check a sector if it is an FAT boot record                            */
/*-----------------------------------------------------------------------*/

/* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */
/* Working buffer */
/* Sector# (lba) to check if it is an FAT boot record or not */

static uint8_t check_fs(uint8_t** buf, uint32_t sect) {
	if (disk_readp_nocopy(buf, sect, 510, 2)) /* Read the boot sector */
		return 3;
	if (LD_WORD(*buf) != 0xAA55) /* Check record signature */
		return 2;

	if (!disk_readp_nocopy(buf, sect, BS_FilSysType, 2) && LD_WORD(*buf) == 0x4146) /* Check FAT12/16 */
		return 0;
	if (_FS_FAT32 && !disk_readp_nocopy(buf, sect, BS_FilSysType32, 2) && LD_WORD(*buf) == 0x4146) /* Check FAT32 */
		return 0;
	return 1;
}

/*--------------------------------------------------------------------------

 Public Functions

 --------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------*/
/* Mount/Unmount a Logical Drive                                         */
/*-----------------------------------------------------------------------*/

/* Pointer to new file system object (NULL: Unmount) */
FRESULT pf_mount(FATFS *fs) {
	uint8_t* buf;
	uint8_t fmt;
	uint32_t bsect, fsize, tsect, mclst;

	FatFs = 0;
	if (!fs)
		return FR_OK; /* Unregister fs object */

	if (disk_initialize() & STA_NOINIT) /* Check if the drive is ready or not */
		return FR_NOT_READY;

	/* Search FAT partition on the drive */
	bsect = 0;
	fmt = check_fs(&buf, bsect); /* Check sector 0 as an SFD format */
	if (fmt == 1) { /* Not an FAT boot record, it may be FDISK format */
		/* Check a partition listed in top of the partition table */
		if (disk_readp_nocopy(&buf, bsect, MBR_Table, 16)) { /* 1st partition entry */
			fmt = 3;
		}
		else {
			if (buf[4]) { /* Is the partition existing? */
				bsect = LD_DWORD(&buf[8]); /* Partition offset in LBA */
				fmt = check_fs(&buf, bsect); /* Check the partition */
			}
		}
	}
	if (fmt == 3)
		return FR_DISK_ERR;
	if (fmt)
		return FR_NO_FILESYSTEM; /* No valid FAT patition is found */

	/* Initialize the file system object */
	int offset = 13;
	if (disk_readp_nocopy(&buf, bsect, offset, sizeof(buf)))
		return FR_DISK_ERR;

	fsize = LD_WORD(buf + (BPB_FATSz16 - offset)); /* Number of sectors per FAT */
	if (!fsize)
		fsize = LD_DWORD(buf + (BPB_FATSz32 - offset));

	fsize *= buf[BPB_NumFATs - offset]; /* Number of sectors in FAT area */
	fs->fatbase = bsect + LD_WORD(buf + (BPB_RsvdSecCnt - offset)); /* FAT start sector (lba) */
	fs->csize = buf[BPB_SecPerClus - offset]; /* Number of sectors per cluster */
	fs->n_rootdir = LD_WORD(buf + BPB_RootEntCnt - (offset)); /* Nmuber of root directory entries */
	tsect = LD_WORD(buf+BPB_TotSec16-13); /* Number of sectors on the file system */
	if (!tsect)
		tsect = LD_DWORD(buf+BPB_TotSec32 - offset);
	mclst = (tsect /* Last cluster# + 1 */
	- LD_WORD(buf+BPB_RsvdSecCnt- offset) - fsize - fs->n_rootdir / 16) / fs->csize + 2;
	fs->n_fatent = (CLUST) mclst;

	fmt = FS_FAT16; /* Determine the FAT sub type */
	if (mclst < 0xFF7) /* Number of clusters < 0xFF5 */
#if _FS_FAT12
		fmt = FS_FAT12;
#else
	return FR_NO_FILESYSTEM;
#endif
	if (mclst >= 0xFFF7) /* Number of clusters >= 0xFFF5 */
#if _FS_FAT32
		fmt = FS_FAT32;
#else
	return FR_NO_FILESYSTEM;
#endif

	fs->fs_type = fmt; /* FAT sub-type */
	if (_FS_FAT32 && fmt == FS_FAT32)
		fs->dirbase = LD_DWORD(buf+(BPB_RootClus-13)); /* Root directory start cluster */
	else
		fs->dirbase = fs->fatbase + fsize; /* Root directory start sector (lba) */
	fs->database = fs->fatbase + fsize + fs->n_rootdir / 16; /* Data start sector (lba) */

	fs->flag = 0;
	FatFs = fs;

	return FR_OK;
}

/*-----------------------------------------------------------------------*/
/* Open or Create a File                                                 */
/*-----------------------------------------------------------------------*/

/* Pointer to the file name */
FRESULT pf_open(const char *path) {
	FRESULT res;
	DIR dj;
	uint8_t sp[12], dir[32];
	FATFS *fs = FatFs;

	if (!fs) /* Check file system */
		return FR_NOT_ENABLED;

	fs->flag = 0;
	dj.fn = sp;
	res = follow_path(&dj, dir, path); /* Follow the file path */
	if (res != FR_OK)
		return res; /* Follow failed */
	if (!dir[0] || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */
		return FR_NO_FILE;

	fs->org_clust = LD_CLUST(dir); /* File start cluster */
	fs->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */
	fs->fptr = 0; /* File pointer */
	fs->flag = FA_OPENED;

	return FR_OK;
}

/*-----------------------------------------------------------------------*/
/* Read File                                                             */
/*-----------------------------------------------------------------------*/
#if _USE_READ

/* Pointer to the read buffer (NULL:Forward data to the stream)*/
/* Number of bytes to read */
/* Pointer to number of bytes read */
FRESULT pf_read(void* buff, uint16_t btr, uint16_t* br) {
	DRESULT dr;
	CLUST clst;
	uint32_t sect, remain;
	uint16_t rcnt;
	uint8_t cs, *rbuff = buff;
	FATFS *fs = FatFs;

	*br = 0;
	if (!fs)
		return FR_NOT_ENABLED; /* Check file system */
	if (!(fs->flag & FA_OPENED)) /* Check if opened */
		return FR_NOT_OPENED;

	remain = fs->fsize - fs->fptr;
	if (btr > remain)
		btr = (uint16_t) remain; /* Truncate btr by remaining bytes */

	while (btr) { /* Repeat until all data transferred */
		if ((fs->fptr % 512) == 0) { /* On the sector boundary? */
			cs = (uint8_t)(fs->fptr / 512 & (fs->csize - 1)); /* Sector offset in the cluster */
			if (!cs) { /* On the cluster boundary? */
				clst = (fs->fptr == 0) ? /* On the top of the file? */
				fs->org_clust : get_fat(fs->curr_clust);
				if (clst <= 1)
					goto fr_abort;
				fs->curr_clust = clst; /* Update current cluster */
			}
			sect = clust2sect(fs->curr_clust); /* Get current sector */
			if (!sect)
				goto fr_abort;
			fs->dsect = sect + cs;
		}
		rcnt = (uint16_t)(512 - (fs->fptr % 512)); /* Get partial sector data from sector buffer */
		if (rcnt > btr)
			rcnt = btr;
		dr = disk_readp(!buff ? 0 : rbuff, fs->dsect, (uint16_t)(fs->fptr % 512), rcnt);
		if (dr)
			goto fr_abort;
		fs->fptr += rcnt;
		rbuff += rcnt; /* Update pointers and counters */
		btr -= rcnt;
		*br += rcnt;
	}

	return FR_OK;

	fr_abort: fs->flag = 0;
	return FR_DISK_ERR;
}
#endif

/*-----------------------------------------------------------------------*/
/* Write File                                                            */
/*-----------------------------------------------------------------------*/
#if _USE_WRITE

/* Pointer to the data to be written */
/* Number of bytes to write (0:Finalize the current write operation) */
/* Pointer to number of bytes written */
FRESULT pf_write(const void* buff, uint16_t btw, uint16_t* bw) {
	CLUST clst;
	uint32_t sect, remain;
	const uint8_t *p = buff;
	uint8_t cs;
	uint16_t wcnt;
	FATFS *fs = FatFs;

	*bw = 0;
	if (!fs)
		return FR_NOT_ENABLED; /* Check file system */
	if (!(fs->flag & FA_OPENED)) /* Check if opened */
		return FR_NOT_OPENED;

	if (!btw) { /* Finalize request */
		if ((fs->flag & FA__WIP) && disk_writep(0, 0))
			goto fw_abort;
		fs->flag &= ~FA__WIP;
		return FR_OK;
	}
	else { /* Write data request */
		if (!(fs->flag & FA__WIP)) /* Round-down fptr to the sector boundary */
			fs->fptr &= 0xFFFFFE00;
	}
	remain = fs->fsize - fs->fptr;
	if (btw > remain)
		btw = (uint16_t) remain; /* Truncate btw by remaining bytes */

	while (btw) { /* Repeat until all data transferred */
		if (((uint16_t) fs->fptr % 512) == 0) { /* On the sector boundary? */
			cs = (uint8_t)(fs->fptr / 512 & (fs->csize - 1)); /* Sector offset in the cluster */
			if (!cs) { /* On the cluster boundary? */
				clst = (fs->fptr == 0) ? /* On the top of the file? */
				fs->org_clust : get_fat(fs->curr_clust);
				if (clst <= 1)
					goto fw_abort;
				fs->curr_clust = clst; /* Update current cluster */
			}
			sect = clust2sect(fs->curr_clust); /* Get current sector */
			if (!sect)
				goto fw_abort;
			fs->dsect = sect + cs;
			if (disk_writep(0, fs->dsect))
				goto fw_abort;
			/* Initiate a sector write operation */
			fs->flag |= FA__WIP;
		}
		wcnt = 512 - ((uint16_t) fs->fptr % 512); /* Number of bytes to write to the sector */
		if (wcnt > btw)
			wcnt = btw;
		if (disk_writep(p, wcnt))
			goto fw_abort;
		/* Send data to the sector */
		fs->fptr += wcnt;
		p += wcnt; /* Update pointers and counters */
		btw -= wcnt;
		*bw += wcnt;
		if (((uint16_t) fs->fptr % 512) == 0) {
			if (disk_writep(0, 0))
				goto fw_abort;
			/* Finalize the currtent secter write operation */
			fs->flag &= ~FA__WIP;
		}
	}

	return FR_OK;

	fw_abort: fs->flag = 0;
	return FR_DISK_ERR;
}
#endif

/*-----------------------------------------------------------------------*/
/* Seek File R/W Pointer                                                 */
/*-----------------------------------------------------------------------*/
#if _USE_LSEEK

/* File pointer from top of file */
FRESULT pf_lseek(uint32_t ofs) {
	CLUST clst;
	uint32_t bcs, sect, ifptr;
	FATFS *fs = FatFs;

	if (!fs)
		return FR_NOT_ENABLED; /* Check file system */
	if (!(fs->flag & FA_OPENED)) /* Check if opened */
		return FR_NOT_OPENED;

	if (ofs > fs->fsize)
		ofs = fs->fsize; /* Clip offset with the file size */
	ifptr = fs->fptr;
	fs->fptr = 0;
	if (ofs > 0) {
		bcs = (uint32_t) fs->csize * 512; /* Cluster size (byte) */
		if (ifptr > 0 && (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */
			fs->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */
			ofs -= fs->fptr;
			clst = fs->curr_clust;
		}
		else { /* When seek to back cluster, */
			clst = fs->org_clust; /* start from the first cluster */
			fs->curr_clust = clst;
		}
		while (ofs > bcs) { /* Cluster following loop */
			clst = get_fat(clst); /* Follow cluster chain */
			if (clst <= 1 || clst >= fs->n_fatent)
				goto fe_abort;
			fs->curr_clust = clst;
			fs->fptr += bcs;
			ofs -= bcs;
		}
		fs->fptr += ofs;
		sect = clust2sect(clst); /* Current sector */
		if (!sect)
			goto fe_abort;
		fs->dsect = sect + (fs->fptr / 512 & (fs->csize - 1));
	}

	return FR_OK;

	fe_abort: fs->flag = 0;
	return FR_DISK_ERR;
}
#endif

/*-----------------------------------------------------------------------*/
/* Create a Directory Object                                             */
/*-----------------------------------------------------------------------*/
#if _USE_DIR

/* Pointer to directory object to create */
/* Pointer to the directory path */
FRESULT pf_opendir(DIR *dj, const char *path) {
	FRESULT res;
	uint8_t sp[12], dir[32];
	FATFS *fs = FatFs;

	if (!fs) { /* Check file system */
		res = FR_NOT_ENABLED;
	}
	else {
		dj->fn = sp;
		res = follow_path(dj, dir, path); /* Follow the path to the directory */
		if (res == FR_OK) { /* Follow completed */
			if (dir[0]) { /* It is not the root dir */
				if (dir[DIR_Attr] & AM_DIR) /* The object is a directory */
					dj->sclust = LD_CLUST(dir);
				else
					/* The object is not a directory */
					res = FR_NO_PATH;
			}
			if (res == FR_OK)
				res = dir_rewind(dj); /* Rewind dir */
		}
		if (res == FR_NO_FILE)
			res = FR_NO_PATH;
	}

	return res;
}

/*-----------------------------------------------------------------------*/
/* Read Directory Entry in Sequence                                      */
/*-----------------------------------------------------------------------*/

/* Pointer to the open directory object */
/* Pointer to file information to return */
FRESULT pf_readdir(DIR *dj, FILINFO *fno) {
	FRESULT res;
	uint8_t sp[12], dir[32];
	FATFS *fs = FatFs;

	if (!fs) { /* Check file system */
		res = FR_NOT_ENABLED;
	}
	else {
		dj->fn = sp;
		if (!fno) {
			res = dir_rewind(dj);
		}
		else {
			res = dir_read(dj, dir);
			if (res == FR_NO_FILE) {
				dj->sect = 0;
				res = FR_OK;
			}
			if (res == FR_OK) { /* A valid entry is found */
				get_fileinfo(dj, dir, fno); /* Get the object information */
				res = dir_next(dj); /* Increment index for next */
				if (res == FR_NO_FILE) {
					dj->sect = 0;
					res = FR_OK;
				}
			}
		}
	}

	return res;
}

#endif /* _USE_DIR */

